package service

import (
	"common/biz"
	"common/logs"
	"common/utils"
	"context"
	"core/dao"
	"core/models/db"
	"core/models/entity"
	"core/repo"
	"encoding/json"
	"fmt"
	"framework/msError"
	game "game/models/request"
	gameRes "game/models/response"
	gameFlee "game_flee/models/request"
	"hall/models/request"
	"hall/models/response"
	"strconv"
	"strings"
	"time"
)

type GameService struct {
	redisDao     *dao.RedisDao
	gameDao      *dao.GameDao
	userDao      *dao.UserDao
	warehouseDao *dao.WarehouseDao
	typeDao      *dao.TypeDao
}

func (s *GameService) GameList(ctx context.Context) *[]response.Game {
	list := s.gameDao.GameList(ctx)
	var data []response.Game
	for _, v := range *list {
		var config []response.GameConf
		for _, val := range v.Conf {
			conf := response.GameConf{
				ID:    val.ID,
				Name:  val.Name,
				Info:  val.Info,
				Value: val.Value,
				Type:  val.Type,
			}
			config = append(config, conf)
		}
		res := response.Game{
			ID:   v.ID,
			Name: v.Name,
			Desc: v.Desc,
			Img:  v.Img,
			Conf: config,
		}
		data = append(data, res)
	}
	return &data
}

func (s *GameService) GameCardInfo(ctx context.Context, uid, gameID uint) (*response.GameRoleInfo, *msError.Error) {
	data := s.gameDao.GameUser(ctx, uid, gameID)
	if data == nil || data.ID == 0 {
		rolePool := s.gameDao.GetGameRolePool(ctx, gameID, 0, true)
		role := db.GameRole{
			GameID: gameID,
			UID:    uid,
			RoleID: rolePool.ID,
			IsUse:  true,
		}
		user := db.GameUser{
			GameID: gameID,
			UID:    uid,
			RoleID: rolePool.ID,
		}
		err := s.gameDao.GameUserCreate(ctx, &user, &role)
		if err != nil {
			return nil, err
		}
		data = s.gameDao.GameUser(ctx, uid, gameID)
	}
	material := s.warehouseDao.GetMaterialInfoByTypeID(ctx, uid, 12)
	mineral := s.warehouseDao.GetMaterialInfoByTypeID(ctx, uid, 11)
	nickname := s.userDao.GetNickname(ctx, uid)
	data.Role.Skill = s.cardSkillDesc(ctx, data.Role.Skill)
	res := &response.GameRoleInfo{
		UID:                   uid,
		Gold:                  material.Num,
		Integral:              data.Integral,
		Mineral:               utils.DecimalIntDiv(mineral.Num, mineral.Types.Val),
		Champion:              data.Champion,
		Runner:                data.Runner,
		ThirdPlace:            data.ThirdPlace,
		Total:                 data.Total,
		Nickname:              nickname,
		RoleName:              data.Role.Name,
		RoleImg:               data.Role.Img,
		RoleAvatar:            data.Role.Avatar,
		RoleDesc:              data.Role.Desc,
		RoleSkillPrice:        data.Role.Skill.Price,
		RoleSkillDesc:         data.Role.Skill.Desc,
		RoleSkillVal:          data.Role.Skill.Val,
		RoleSkillType:         data.Role.Skill.Type,
		RoleSkillLevel:        data.Role.Skill.Level,
		RoleSkillLinkCardType: data.Role.Skill.LinkCardType,
		RoleSkillNotCardType:  data.Role.Skill.NotCardType,
	}
	return res, nil
}

// GetCardInfo 获取卡牌详情
func (s *GameService) GetCardInfo(ctx context.Context, typeId uint, types int) *db.GameCard {
	return s.gameDao.GetCardInfo(ctx, typeId, types)
}

// GetConfVal 获取配置详情
func (s *GameService) GetConfVal(ctx context.Context, gameID uint, name string) string {
	return s.gameDao.GetConfVal(ctx, gameID, name)
}

// cardSkillDesc 替换技能详情数值
func (s *GameService) cardSkillDesc(ctx context.Context, skill db.GameRoleSkill) db.GameRoleSkill {
	if skill.Val > 0 && skill.ID != 1 && skill.ID != 6 {
		skill.Desc = strings.Replace(skill.Desc, "{val}", fmt.Sprintf("+%d", skill.Val), -1)
	} else {
		skill.Desc = strings.Replace(skill.Desc, "{val}", fmt.Sprintf("%d", skill.Val), -1)
	}
	skill.Desc = strings.Replace(skill.Desc, "{level}", fmt.Sprintf("%d", skill.Level), -1)
	var cardTypeMap = map[int]string{
		1: "怪兽卡",
		2: "吞噬卡",
		3: "防具卡",
		4: "武器卡",
		5: "升级卡",
	}

	if skill.LinkCardType > 0 {
		//LinkCardType := s.GetCardInfo(ctx, 0, skill.LinkCardType)
		//skill.Desc = strings.Replace(skill.Desc, "{link_card_type}", LinkCardType.Name, -1)
		skill.Desc = strings.Replace(skill.Desc, "{link_card_type}", cardTypeMap[skill.LinkCardType], -1)
	}
	if skill.NotCardType > 0 {
		//NotCardType := s.GetCardInfo(ctx, 0, skill.NotCardType)
		//skill.Desc = strings.Replace(skill.Desc, "{not_card_type}", NotCardType.Name, -1)
		skill.Desc = strings.Replace(skill.Desc, "{not_card_type}", cardTypeMap[skill.NotCardType], -1)
	}
	return skill
}

func (s *GameService) GameMyRolePool(ctx context.Context, uid, gameID uint) *[]response.GameMyRolePool {
	data := s.gameDao.GameRoles(ctx, uid, gameID)
	var rolePools []response.GameMyRolePool
	for _, v := range *data {
		v.Pool.Skill = s.cardSkillDesc(ctx, v.Pool.Skill)
		rolePool := response.GameMyRolePool{
			RoleID:         v.RoleID,
			RoleName:       v.Pool.Name,
			RoleImg:        v.Pool.Img,
			RoleAvatar:     v.Pool.Avatar,
			RoleDesc:       v.Pool.Desc,
			RoleSkillPrice: v.Pool.Skill.Price,
			RoleSkillDesc:  v.Pool.Skill.Desc,
			IsUse:          v.IsUse,
		}
		rolePools = append(rolePools, rolePool)
	}
	return &rolePools
}

func (s *GameService) GameRolePool(ctx context.Context, uid uint, gameID uint) *[]response.GameRolePool {
	data := s.gameDao.GameRolePool(ctx, gameID)
	var rolePools []response.GameRolePool
	for _, v := range *data {
		v.Skill = s.cardSkillDesc(ctx, v.Skill)
		isHave := false
		myRole := s.gameDao.GetGameMyRole(ctx, uid, gameID, v.ID)
		if myRole != nil && myRole.ID != 0 {
			isHave = true
		}
		rolePool := response.GameRolePool{
			RoleID:       v.ID,
			RoleName:     v.Name,
			RoleImg:      v.Img,
			RoleAvatar:   v.Avatar,
			RoleDesc:     v.Desc,
			RolePrice:    v.Price,
			RoleTypeID:   v.TypeID,
			RoleTypeName: v.Types.Name,
			RoleTypeImg:  v.Types.Img,
			RoleIsGifts:  v.IsGifts,
			IsHave:       isHave,
			IsBuy:        v.IsBuy,
		}
		rolePool.Skill.RoleSkillPrice = v.Skill.Price
		rolePool.Skill.RoleSkillDesc = v.Skill.Desc
		rolePools = append(rolePools, rolePool)
	}
	return &rolePools
}

func (s *GameService) GameBuyRole(ctx context.Context, uid uint, req *request.GameCardRole) *msError.Error {
	rolePool := s.gameDao.GetGameRolePool(ctx, req.ID, req.RoleID, false)
	if rolePool == nil || rolePool.ID == 0 {
		logs.Error("[Game]用户%d BuyRole GameBuyRole err:查询角色错误 req=%v", uid, req)
		return biz.RequestDataError
	}
	myRole := s.gameDao.GetGameMyRole(ctx, uid, req.ID, req.RoleID)
	if myRole != nil && myRole.ID != 0 {
		logs.Error("[Game]用户%d BuyRole GameBuyRole err:重复购买角色 req=%v", uid, myRole)
		return biz.GameRoleError
	}
	material := s.warehouseDao.GetMaterialInfoByTypeID(ctx, uid, 12)
	if material == nil || material.ID == 0 {
		logs.Error("[Game]用户%d BuyRole GameBuyRole err:查询材料表失败 req=%v", uid, material)
		return biz.RequestDataError
	}
	if material.Num < rolePool.Price {
		logs.Error("[Game]用户%d BuyRole GameBuyRole err:游戏币不足 req=%v", uid, material)
		return biz.NotEnoughGold
	}
	return s.gameDao.GameBuyRole(ctx, material, rolePool)
}

func (s *GameService) GameUseRole(ctx context.Context, uid uint, req *request.GameCardRole) *msError.Error {
	myRole := s.gameDao.GetGameMyRole(ctx, uid, req.ID, req.RoleID)
	if myRole == nil || myRole.ID == 0 {
		logs.Error("[Game]用户%d BuyRole GameBuyRole err:该角色未拥有 req=%v", uid, req)
		return biz.GameNotRoleError
	}
	if myRole.IsUse {
		logs.Error("[Game]用户%d BuyRole GameBuyRole err:该角色已使用，重复操作 req=%v", uid, myRole)
		return biz.SqlError
	}
	return s.gameDao.GameUseRole(ctx, uid, req)
}

func (s *GameService) GameExchange(ctx context.Context, uid uint, req *request.GameExchange) *msError.Error {
	types := s.typeDao.GetInfo(ctx, 12)
	warehouse := s.warehouseDao.GetMaterialInfoByTypeID(ctx, uid, req.TypeID)
	exchangeLog := &entity.GameCardExchange{
		GameID:     req.ID,
		UID:        uid,
		Types:      req.Type,
		TypeID:     req.TypeID,
		TypeName:   warehouse.Types.Name,
		TypeImg:    warehouse.Types.Img,
		TypeModule: warehouse.Types.Module,
		Count:      req.Num,
		AddTime:    time.Now().Format(time.DateTime),
	}
	if req.Type == 1 { //兑换游戏币
		//查询材料是否充足
		if warehouse.Num < req.Num {
			logs.Error("[Game]用户%d GameExchange err:材料不足 req=%v,data=%v", uid, req, warehouse)
			return biz.NotEnoughGold
		}
		val := s.GetConfVal(ctx, req.ID, "exchange_rate")
		intVal, _ := strconv.Atoi(val)
		total := int64(intVal) * req.Num
		exchangeLog.Num = total
	} else { //游戏币兑现
		val := s.GetConfVal(ctx, req.ID, "game_exchange_rate")
		intVal, _ := strconv.Atoi(val)
		total := int64(intVal) * req.Num
		warehouseGame := s.warehouseDao.GetMaterialInfoByTypeID(ctx, uid, types.ID)
		if warehouseGame.Num < total {
			logs.Error("[Game]用户%d GameExchange err:材料不足 req=%v,data=%v", uid, req, warehouse)
			return biz.NotEnoughGold
		}
		exchangeLog.Num = -total
	}
	return s.gameDao.GameExchange(ctx, exchangeLog, types)
}

func (s *GameService) GetConf(ctx context.Context, gameID uint) *[]db.GameConf {
	return s.gameDao.GetConf(ctx, gameID)
}

func (s *GameService) GetConfInfo(ctx context.Context, gameID uint, name string) *db.GameConf {
	return s.gameDao.GetConfInfo(ctx, gameID, name)
}

func (s *GameService) GameCard(ctx context.Context, gameID uint) *[]db.GameCard {
	data := s.gameDao.GetGameCard(ctx, gameID)
	return data
}

func (s *GameService) GetFleeUsersDiff(ctx context.Context, diff int64, rounds int64) *[]response.GameFleeInfo {
	users := s.userDao.GetFleeUsersDiff(ctx)
	var res []response.GameFleeInfo
	dates := utils.TimeOrDateAsYear(0, "", "Y-m-d")
	for _, v := range *users {
		if diff <= 0 {
			break
		}
		if v.Role.ID == 0 {
			continue
		}
		////查看当前时间是否已下注
		bettingLog := s.gameDao.GameFleeLogCount(ctx, v.ID, rounds, dates)
		if bettingLog > 0 {
			continue
		}
		material := s.warehouseDao.GetMaterialInfoByTypeID(ctx, v.ID, 12)
		res = append(res, response.GameFleeInfo{
			UID:      v.ID,
			Nickname: v.Role.Nickname,
			Gold:     material.Num,
		})
		s.userDao.UpRoleIsUse(ctx, v.ID)
		diff--
	}
	return &res
}

func (s *GameService) GameFleeInfo(ctx context.Context, uid uint) (*response.GameFleeInfo, *msError.Error) {
	user := s.userDao.GetUserOfRole(ctx, uid)
	if user == nil || user.ID == 0 {
		return nil, biz.SqlError
	}
	material := s.warehouseDao.GetMaterialInfoByTypeID(ctx, uid, 12)
	if material == nil || material.ID == 0 {
		return nil, biz.RequestDataError
	}
	return &response.GameFleeInfo{
		UID:        user.ID,
		InviteCode: user.InviteCode,
		Nickname:   user.Role.Nickname,
		Gold:       material.Num,
	}, nil
}

func (s *GameService) FleeBetting(ctx context.Context, roomID, myRoomID uint, roomName string, uid uint, rounds, num int64, isRobot bool) (int64, *msError.Error) {
	betting, err := s.gameDao.FleeBetting(ctx, roomID, myRoomID, roomName, uid, rounds, num, isRobot)
	if err != nil {
		return 0, err
	}
	errs := s.redisDao.RSet(ctx, fmt.Sprintf("flee_change_room_%v_%v", uid, rounds), "1", 5)
	if errs != nil {
		logs.Error("[Game]用户%d FleeBetting err:redis添加错误 err=%v", uid, errs)
		return 0, biz.SqlError
	}
	return betting, nil
}

func (s *GameService) FleeChangeRoomBool(ctx context.Context, uid uint, rounds int64) bool {
	res := s.redisDao.RGet(ctx, fmt.Sprintf("flee_change_room_%v_%v", uid, rounds))
	var resBool bool
	if res != "" {
		resBool = true
	}
	return resBool
}

func (s *GameService) FleeChangeRoom(ctx context.Context, roomID, myRoomID uint, roomName string, uid uint, rounds int64) *msError.Error {
	err := s.gameDao.FleeChangeRoom(ctx, roomID, myRoomID, roomName, uid, rounds)
	if err != nil {
		return err
	}
	errs := s.redisDao.RSet(ctx, fmt.Sprintf("flee_change_room_%v_%v", uid, rounds), "1", 5)
	if errs != nil {
		logs.Error("[Game]用户%d FleeChangeRoom err:redis添加错误 err=%v", uid, errs)
		return biz.SqlError
	}
	return nil
}

func (s *GameService) KillRoom(ctx context.Context, rounds int64, roomId uint, roomName string) (*entity.GameFleeKillRoom, error) {
	return s.gameDao.KillRoom(ctx, rounds, roomId, roomName)
}

func (s *GameService) EndGameCard(ctx context.Context, users []byte, roomID string, total int64, peopleNum int, champion float64, runner float64, place float64) error {
	var usersData []game.EndGameUsers
	_ = json.Unmarshal(users, &usersData)
	return s.gameDao.EndGameCard(ctx, &usersData, roomID, total, peopleNum, champion, runner, place)
}

func (s *GameService) EndGameCardOne(ctx context.Context, uid uint, roomID string) error {
	return s.gameDao.EndGameCardOne(ctx, uid, roomID)
}

func (s *GameService) EndGameFlee(ctx context.Context, rounds int64) (*[]entity.GameFleeRoomBetting, error) {
	users := s.gameDao.GameFleeUsers(ctx, rounds)
	killTotal := s.gameDao.GameFleeKillTotal(ctx, rounds)
	winTotal := s.gameDao.GameFleeWinTotal(ctx, rounds)
	winCount := s.gameDao.GameFleeWinCount(ctx, rounds)
	return s.gameDao.EndGameFlee(ctx, rounds, users, winCount, winTotal, killTotal)
}

func (s *GameService) GetLastFleeBetting(ctx context.Context, rounds int64) *entity.GameFleeRoomBetting {
	return s.gameDao.GetLastFleeBetting(ctx, rounds)
}

func (s *GameService) GetFleeBetting(ctx context.Context, uid uint, rounds int64) *entity.GameFleeRoomBetting {
	return s.gameDao.GetFleeBetting(ctx, uid, rounds)
}

func (s *GameService) FleeLog(ctx context.Context, uid uint, req *gameFlee.PageMsg) (int64, *[]gameRes.FleeLog) {
	count, data := s.gameDao.GameFleeLog(ctx, uid, req)
	var res []gameRes.FleeLog
	for _, v := range *data {
		killRoom := s.gameDao.KillRoomAll(ctx, v.Rounds, v.Dates)
		var killRooms string
		if len(*killRoom) > 0 {
			for _, val := range *killRoom {
				killRooms += fmt.Sprintf("%v,", val)
			}
			killRooms = killRooms[:len(killRooms)-1]
		} else {
			killRooms = "无"
		}
		var randsReward []gameRes.RandsReward
		for _, val := range v.RandsReward {
			randsReward = append(randsReward, gameRes.RandsReward{
				TypeID:     val.TypeID,
				TypeName:   val.TypeName,
				TypeImg:    val.TypeImg,
				TypeModule: val.TypeModule,
				Num:        val.Num,
			})
		}
		res = append(res, gameRes.FleeLog{
			RoomID:                v.RoomID,
			RoomName:              v.RoomName,
			Rounds:                v.Rounds,
			KillRoom:              killRooms,
			Num:                   v.Num,
			TypeName:              v.TypeName,
			TypeImg:               v.TypeImg,
			ReturnNum:             v.ReturnNum,
			Reward:                v.Reward,
			IsKill:                v.IsKill,
			IsDecline:             v.IsDecline,
			DeclineRewardTypeID:   v.DeclineRewardTypeID,
			DeclineRewardTypeName: v.DeclineRewardTypeName,
			DeclineRewardTypeImg:  v.DeclineRewardTypeImg,
			DeclineReward:         v.DeclineReward,
			RandsReward:           randsReward,
			Dates:                 v.AddTime,
		})
	}
	return count, &res
}

func (s *GameService) FleeKillCount(ctx context.Context, id uint, rounds int64) int64 {
	var minRounds int64
	if rounds > 50 {
		minRounds = rounds - 50
	} else {
		rounds = 0
	}
	return s.gameDao.FleeKillCount(ctx, id, minRounds, rounds)
}

func (s *GameService) CardLog(ctx context.Context, uid uint, req *game.PageMsg) (int64, *[]gameRes.CardLog) {
	count, data := s.gameDao.GameCardLog(ctx, uid, req)
	var res []gameRes.CardLog
	for _, v := range *data {
		peopleNum := s.gameDao.GameCardRoomCount(ctx, v.RoomID, v.Dates)
		betting := new(entity.GameCardRoomLog)
		betting = s.gameDao.GetCardBetting(ctx, uid, v.RoomID, v.Dates)
		res = append(res, gameRes.CardLog{
			Dates:            v.AddTime,
			RoomID:           v.RoomID,
			PeopleNum:        peopleNum,
			Ranking:          v.Ranking,
			Num:              betting.Num,
			TypeName:         betting.TypeName,
			TypeImg:          betting.TypeImg,
			IntegralTypeName: v.IntegralTypeName,
			IntegralTypeImg:  v.IntegralTypeImg,
			Integral:         v.Integral,
			DebrisTypeName:   v.DebrisTypeName,
			DebrisTypeImg:    v.DebrisTypeImg,
			Debris:           v.Debris,
			RewardTypeName:   v.RewardTypeName,
			RewardTypeImg:    v.RewardTypeImg,
			RewardNum:        v.RewardNum,
			IsHangUp:         v.IsHangUp,
		})
	}
	return count, &res
}

func (s *GameService) FleeRobotConf(ctx context.Context, second int) *db.GameFleeRobot {
	return s.gameDao.FleeRobotConf(ctx, second)
}

func (s *GameService) GameCardNotEnd(ctx context.Context, uid uint) int64 {
	return s.gameDao.GameCardNotEnd(ctx, uid)
}

func NewGameService(r *repo.Manager) *GameService {
	return &GameService{
		gameDao:      dao.NewGameDao(r),
		redisDao:     dao.NewRedisDao(r),
		userDao:      dao.NewUserDao(r),
		warehouseDao: dao.NewWarehouseDao(r),
		typeDao:      dao.NewTypeDao(r),
	}
}
